test(objectql): the "not tenant-scoped" DataEvent pin measures the withheld column instead of claiming it - #16124
Merged
Conversation
…thheld column instead of claiming it The `#14970` block's pin `an object that is not tenant-scoped OMITS the key on all three actions` was written on `task` in the belief that `task` declares no `organization_id`. `registerObject` INJECTS the kernel column (`TENANT_SCOPE_FIELD_DEF`, gated on the injection plan's `tenant` flag), so on the registered `task` `resolveTenantFieldName` answers `organization_id`. The pin passed — correctly — for a different reason than it stated: the ROW carried no organization, not the object no column. Measured on this base: registered `task` carries `organization_id`; `resolveTenantFieldName` = `organization_id`. An object declaring `tenancy: { enabled: false }` has the column withheld and resolves `null`. The sentence becomes an assertion: * a new `audit_note` fixture declaring `tenancy: { enabled: false }` carries the genuinely-unscoped pin, and asserts the column was WITHHELD (with the registered `task` as the control that it is otherwise INJECTED) before asserting the omission; * the old `task` case is kept as its own pin and described as what it measures — an injection-scoped object whose rows carry no organization; * the neighbouring `invoice` pin's "distinct from the case above" clause is repaired in the same pass, since the pin above it changed. No runtime change, no assertion weakened, `#14970` not reopened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 6, 2026
zhuangjianguo
marked this pull request as ready for review
September 6, 2026 03:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15688
What was wrong
The
#14970block inpackages/objectql/src/engine-data-events.test.tscarried a pin whose stated reason was false:registerObjectinjects the kernelorganization_idcolumn (TENANT_SCOPE_FIELD_DEF, gated on the injection plan'stenantflag) into every object that does not declaretenancy: { enabled: false }. So on the registeredtaskthe resolver answersorganization_id, notnull. The pin passed — correctly — because the ROW carried no organization. No assertion was wrong; the explanation was, and the next author copying it builds a "not tenant-scoped" fixture out of an object that is walled.#14970is not reopened by this and this body carries no closing keyword for it. That block stays correct.Measured on this base (
9b459b791), before writing anythingA throwaway harness in
packages/objectql/src, run and then deleted (tree proven back to HEAD):taskcolumns["organization_id","created_at","created_by","updated_at","updated_by","owner_id","owning_business_unit_id","id","title","status"]tenancy: { enabled: false }object columnsorganization_idresolveTenantFieldName(registered task)"organization_id"resolveTenantFieldName(registered opt-out object)null⇒ the card's core reading is confirmed, in both directions.
The fix — route 2, the durable one
Triage offered a reword or a fixture swap and declined to rule; the swap turns the sentence into an assertion, so that is what this does.
audit_notefixture declaringtenancy: { enabled: false }carries the genuinely-unscoped pin. The pin now asserts the premise before using it: the registry WITHHELDorganization_idonaudit_note, with the registeredtaskas the control that it is otherwise INJECTED. A future edit that moves the injection reddens here instead of leaving a stale sentence.taskcase is kept as its own pin — no coverage is lost — and described as what it measures: an injection-scoped object whose rows carry no organization, omitting the key on all three actions.invoicepin'sDistinct from the case aboveclause is repaired in the same pass, named here rather than smuggled in: the pin above it changed, so that clause became false as a direct consequence of this edit. It now names the real distinction — injected column with the row silent, versus author-declared column with the row explicitlynull.Comment only plus one fixture, inside one test file. No runtime change; no tenant-injection behaviour touched; no test skipped, weakened or quarantined.
A claim in the card this PR did NOT inherit
The card carried one reading triage explicitly did not reproduce: "a predicate write on the registered
taskunder anisolatedwall had the batch key stamped, precisely because the object IS tenant-scoped once registered." Measured here rather than cited:task, posture providerisolated, member context with an active organization, no recorded verdict ⇒organizationIdABSENT (hasOwnPropertyfalse).{ kind: 'organization', organizationId: 'org_acme' }verdict ⇒ key stampedorg_acme.So on this tree the batch key follows the recorded Layer 0 verdict alone, never the object's tenant-scoping —
bulkEventOrganizationIdreads onlyTenantLayer0VerdictSchemasince the mirror was deleted (#15706 / #15813, ruled 2026-09-05). The reading was true of the producer that existed when it was taken and is false of this one. Nothing in the new comments asserts it. The neighbouring half of that sentence does still hold by source reading:security-plugin.tsanswersobjectHasOrgIdFieldoff the registered field set, so a registeredtaskis walled.Verification
All at
5cc619435.pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/engine-data-events.test.ts— 41 passed before, 42 passed after (one pin added).tenancy: { enabled: false }from the new fixture, i.e. exactly the mistake this card closes. Mutation proven on disk before measuring (anchored count 1 to 0; blobce432926vs HEAD blobfc764bb5), then:1 failed | 41 passed,AssertionError: expected [ 'organization_id', ...(8) ] to not include 'organization_id'on the new pin. Restore proven after: blob back tofc764bb5andgit diff HEADempty. No rebuild leg is owed — the mutation is in the test file itself and the subject is imported relative within the same package, so nothing resolves throughdist/.pnpm --filter @objectstack/objectql typecheck— green, and it really covers this file: the build config excludes**/*.test.ts, butcheck:test-typecheckrunstsconfig.test.json,tsc --listFilesputs this file in that program, and its exact shrink-only ledger entry (TS6133: 'warn' ...x1) held.44 file(s) / 242 error(s) / 69 pinned signature(s)unmoved.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reads the change set itself): 44 exit 0. The two others arePREREQUISITE NOT MET(exit 3, NOT MEASURED, not red):check:dual-build-cjs-loadsandcheck:type-check-debt --re-measureboth need the whole workspace built. CI builds it; the per-package test-typecheck above is the same file under the same strictness.eslint . --no-inline-config --format json: 6091 files, 0 errors, 0 warnings, exit 0. Not a narrowed run.No changeset —
skip-changesetMeasured, not assumed:
packages/objectqlpublishesfiles: ["dist", "README.md", "CHANGELOG.md"], its tsup entries aresrc/index.tsandsrc/core.tsonly, and neither entry reaches this file. A*.test.tscannot enterdist/, so this diff publishes nothing from any released package — which is what the label is for, per AGENTS.md.Clause-②: no, re-derived from this diff and not inherited: one file,packages/objectql/src/engine-data-events.test.ts,+73 / -10. No schema, no export, no accept/reject behaviour, nothing published moves.🤖 Generated with Claude Code
Generated by Claude Code